home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / wmv12s.zip / FRONT.C < prev    next >
Text File  |  1993-01-04  |  14KB  |  447 lines

  1. /* Program to move files and/or subdirectories - like mv on unix
  2. ** Written by Peter Wu July, 86 @ Faculty Support Center @ UW-Madison
  3. ** Compile with IBM C version 1.00 (or Microsoft C 3.00).
  4. ** Link with fstat, mv, normal, sector, absdr, func32h, break, putn /stack:5000
  5. **
  6. ** This is module "front.c", the front end of mv. It looks at the sources
  7. ** and destination supplied by the user and determines whether to call
  8. ** mvdir to move/rename sub-directories or call rename to move/rename files.
  9. */
  10.  
  11. /* programmer's notes:
  12. -- won't work on network disk
  13. --
  14. -- things to work on:
  15. --
  16. -- be interactive:
  17. --   ask if user wants to replace existing file/directory
  18. --   verbose mode where user must confirm each move
  19. -- better error handling
  20. -- make it work on network disks
  21. -- detect and report write-protected disk if possible
  22. -- allow /f option on destination?
  23. --
  24. -- bugs:
  25. +--------------------------------+
  26. |  c:                 |
  27. |  subst p: /user/peter/mv/test  |
  28. |  mv /usr/peter/mv/test .     |
  29. +--------------------------------+
  30. -- The above sequence will prevent mv from removing the source directory after
  31. -- the content is moved to the destination directory. I don't know any way to
  32. -- detect this BEFORE moving the content. So my solution is to print a message
  33. -- telling the user to remove the source directory (now empty) by hand after
  34. -- he got rid of the 'subst drive'.
  35. */
  36. #define LINT_ARGS
  37.  
  38. #define UNK_TYP 0
  39.  
  40. #define A_INT 0x80  /* interactive file attribute */
  41.  
  42. #define LASTDRV 26  /* drive# larger than this is network share disk */
  43. #define NULL (char *) 0
  44. #define PLEN 200    /* max. path length; hope it's long enough */
  45.  
  46. #include "dta.h"
  47. #include "date.h"
  48. #include <conio.h>
  49. #include <string.h>
  50. #include <stdlib.h>
  51.  
  52. char lastc(char *);
  53. char getkey(char *);
  54.  
  55. int brk_st;  /* original break status: 0=break off, 1=break on */
  56.  
  57.   /* When break is on, user can break the program when any DOS function is
  58.   ** called. When break is off, user can only break the program when a DOS
  59.   ** I/O function is called (if the user presses break before an I/O function,
  60.   ** DOS will remember the break but let the program keep running until the
  61.   ** program calls an IO function). It is undesirable to have break on
  62.   ** all the time since for instance if the user breaks the program after I
  63.   ** wrote one sector to disk (I need to write three sectors per sub-directory
  64.   ** moved), he'll end up with an inconsistent directory. The solution is to
  65.   ** make sure break is off when I'm doing disk writes, and restore it
  66.   ** to its orginal status when I'm done writing the disk.
  67.   ** If the user really wants to mess up his disk, he can still do it with
  68.   ** ctrl-alt-del or switching the power off in the middle of running mv.
  69.   */
  70.  
  71. main(argc,argv)
  72. int argc;
  73. char *argv[];
  74. {
  75.   unsigned int status, dtype, stype, status2, smask, dmask, i, drv, plimit,
  76.            doit;
  77.   union dtbuf mydta1, mydta2, tmpdta;
  78.   char fsource[PLEN], fdest[PLEN], *source, *dest, *tmp, fdest2[PLEN];
  79.  
  80.   if (argc < 3) {  /* print help */
  81.     putn(
  82.  
  83. "Usage: MV <source1> <source2> .. <sourceN> <dest>\n\15",
  84. "  MV renames/moves the source files/directories to the destination.\n\15",
  85. "  Wildcards ok. Specify source type with /d, /f, /h, and /i.\n\15",
  86. "  /d=sub-directories, /f=files, /h=search hidden, /i=interactive.\n\15",
  87. "    \"*.*\\.\" or \"*.*/d\" specifies all sub-directories only\n\15",
  88. "    \"*.*/f\" specifies all visible files only\n\15",
  89. "    \"*.*/hf\" specifies all visible and hidden files\n\15",
  90. "    \"*.*/fi\" will prompt you (move or not) for every file found\n\15",
  91. "  Version 1.20 made ", date, ". For DOS 2.xx and 3.xx.\n\n\15",
  92. "Please send comments/bug reports to one of the following addresses:\n\15",
  93. "  Arpanet: pwu@unix.macc.wisc.edu\n\15",
  94. "  Bitnet: WU at WISVMACC\n\15",
  95. "  CompuServe: 76377,1332\n\15",
  96. "  UUCP: {akgua|ihnp4|seismo|harvard|allegra|ucbvax}!uwvax!uwmacc!pwu\n\15",
  97. NULL);
  98.  
  99.     exit(0);
  100.   }
  101.  
  102.   /* test DOS version */
  103.   switch (_osmajor) {
  104.     case 2:  /* dos 2.xx */
  105.       break;
  106.     case 3:  /* dos 3.xx */
  107.       if (_osminor <= 20) {  /* make sure it's no later than version 3.20 */
  108.     break;
  109.       }
  110.     default:
  111.       cputs("need DOS between version 2.00 and 3.20\n\15");
  112.       exit(1);
  113.   }
  114.  
  115.   plimit = PLEN - 80;  /* limit on user supplied path name */
  116.  
  117.   /* process destination first */
  118.   dest = argv[argc-1];
  119.   if (strlen(dest) > plimit) {
  120.     cputs("destination path too long\n\15");
  121.     exit(1);
  122.   }
  123.  
  124.   strcpy(fdest,dest);
  125.   dtype = normal(fdest);  /* normalize destination path */
  126.  
  127.   if (dtype == 0) {  /* no specified type for destination */
  128.     dmask = A_FIL | A_DIR;
  129.   } else {
  130.     dmask = dtype;
  131.   }
  132.  
  133.   status2 = ffmf(fdest, A_MASK, &mydta2);  /* find info on dest */
  134. #ifdef debug
  135.   printf("fdest is %s, dmask=%d, status2 = %d\n", fdest, dmask, status2);
  136. #endif
  137.  
  138.   if (!status2) {  /* if destination exists  */
  139. #ifdef debug
  140.     printf("destination exists and has attr: %x\n", mydta2.dos.attr);
  141. #endif
  142.     /* check if destination is ambigious here */
  143.     if (lastc(fdest) != '\\') { /* root would cause error, so don't check it */
  144.       tmpdta = mydta2;    /* don't disturb mydta2, we need it later */
  145.       status = fnmf(&tmpdta);
  146.       if (!status) {  /* ha, there's more than one destination! */
  147.     cputs("destination is ambigious\n\15");
  148.     exit(1);
  149.       }
  150.     }
  151.  
  152.     /* if destination is an existing file, report error */
  153.     if ((mydta2.dos.attr & A_DIR) == 0) {
  154.       cputs("destination is an existing file!\n\15");
  155.       exit(1);
  156.     }
  157.  
  158.     /* fix fdest so that a destination of "*\." will have the expanded name
  159.     ** by removing the * and appending the directory name found by ffmf.
  160.     ** This will cause an error if fdest is the root of a 'subst' disk,
  161.     ** so we must make sure fdest is not a root.
  162.     */
  163.     if (lastc(fdest) != '\\') {  /* if not root then */
  164.       tmp = strrchr(fdest,'\\');  /* find last '\' */
  165.       if (tmp == NULL) {  /* no '\' ????!!!! */
  166.     cputs("error after strrchr: cannot find \\\n\15");
  167.     error("front",0);
  168.       }
  169.       strcpy(tmp+1, mydta2.dos.fn);  /* dest with wild cards expanded */
  170.     }
  171.  
  172.   } else {  /* destination not found */
  173.  
  174. #ifdef debug
  175.     printf("destination not exists\n");
  176. #endif
  177.  
  178.     if (dtype == A_DIR) {  /* specified directory not exist */
  179.       cputs("can't find destination directory\n\15");
  180.       exit(1);
  181.     } else {  /* destination type not specified or of file type */
  182.       /* check: source better be unambigious */
  183.       if (argc > 3) {
  184.     cputs("can't rename more than one source\n\15");
  185.     exit(1);
  186.       }
  187.       /* destination is not found, let's lookup destination's parent.
  188.       ** This has to exist (e.g. if C:\X\Y is destination and doesn't
  189.       ** exist, C:\X should still exists). This lookup also allows us
  190.       ** to compare the drive number between the destination and the
  191.       ** source to detect cross device move.
  192.       */
  193.       strcpy(fdest2, fdest);
  194.       chopath(fdest2);    /* remove last portion of path */
  195.       status = ffmf(fdest2, A_MASK, &mydta2);
  196.       if (status) {  /* if destination's parent doesn't exist */
  197.     putn("path \"", fdest2, "\" not exist!\n\15", 0);
  198.     exit(1);
  199.       } else if ((mydta2.dos.attr & A_DIR) == 0) { /* not a directory */
  200.     putn("\"", fdest2, "\" is not a directory!\n\15", 0);
  201.     exit(1);
  202.       }
  203.     }
  204.   }
  205.  
  206.   brk_st = bstat();  /* get current break status (on or off) */
  207.  
  208.   /***********************************************************/
  209.   /* now process SOURCE **************************************/
  210.   /***********************************************************/
  211.   for (i=1; i < argc - 1; i++) {  /* for all source specification */
  212.     source = argv[i];
  213.     if (strlen(source) > plimit) {  /* rare user error but just in case */
  214.       cputs("source path too long! skipped\n\15");
  215.       continue;  /* next argument */
  216.     }
  217.  
  218.     strcpy(fsource,source);
  219.     stype = extype(fsource);  /* extract type */
  220.     stype |= normal(fsource);  /* normalize source path */
  221.  
  222. #ifdef debug
  223.     printf("normalized source path: %s\n", fsource);
  224. #endif
  225.  
  226.     if ((stype & (A_DIR | A_FIL)) == 0) {  /* no specified type, use default */
  227.       smask = stype | A_FIL | A_DIR;  /* look for file or directory */
  228.     } else {
  229.       smask = stype;
  230.     }
  231.  
  232.     status = ffmf(fsource, smask, &mydta1);  /* find info on source */
  233.     if (status) {
  234.       putn(source, " not found\n\15", 0);
  235.       continue;
  236.     }
  237.  
  238.     /* see if source is on a network disk */
  239.     if ((mydta1.dos.drv_no < 0) || (mydta1.dos.drv_no >= LASTDRV)) {
  240.       cputs("sorry, mv doesn't work on network disks\n\15");
  241.       exit(1);
  242.     }
  243.  
  244.     /* is source and destination on same drive? */
  245.     if (mydta1.dos.drv_no != mydta2.dos.drv_no) {
  246.       cputs("source and destination must be on same physical drive\n\15");
  247.       exit(1);
  248.     }
  249.  
  250.     /* check: if destination does not exist, source better be unique */
  251.     if (status2) {  /* destination does not exist */
  252.       if (lastc(fsource) != '\\') { /* don't call fnmf on root */
  253.     tmpdta = mydta1;  /* do not change mydta1, mess with a copy */
  254.     status = fnmf(&tmpdta);  /* see if there's more than one source */
  255.     if (!status) {    /* source is ambigious */
  256.       cputs("can't rename more than one source\n\15");
  257.       exit(1);
  258.     }
  259.       }
  260.     }
  261.  
  262.     do {  /* repeat for all source wildcard */
  263.  
  264. #ifdef debug
  265.       printf("source found, fn: %s\n", mydta1.dos.fn);
  266.       printf("found attribute is %2xh\n", mydta1.dos.attr);
  267. #endif
  268.  
  269.       /* form source name with wild cards expanded */
  270.       chopath(fsource);  /* chop off wild cards */
  271.       catpath(fsource, mydta1.dos.fn);    /* append expanded name */
  272.  
  273.       if (status2) {  /* destination not exist */
  274.  
  275.     if (stype & A_INT) {  /* interactive mode */
  276.       doit = prompt(fsource,fdest);
  277.     } else {  /* in batch mode, always do it */
  278.       putn(fsource, " ── ", fdest, "  ", 0);
  279.       doit = 1;  /* do it! */
  280.     }
  281.  
  282.     if (doit) {
  283.  
  284.       if (mydta1.dos.attr & A_DIR) {  /* rename or move directory */
  285.  
  286.         status =  mvdir(mydta1, fsource, fdest);
  287.         if (!status) {
  288.           cputs("OK.\n\15");
  289.         }
  290.  
  291.       } else {  /* rename or move file */
  292.  
  293.         /* don't know what rename does when user presses break key,
  294.         ** so set break off to be safe
  295.         */
  296.         bset(0);  /* turn break off (disable break key) */
  297.         status = rename(fdest,fsource);  /* use fdest or dest? */
  298.         bset(brk_st);  /* restore break status to orginal value */
  299.  
  300.         if (status) {
  301.           putn("can't\n\15",
  302.            "invalid file name/disk write protected\n\15", 0);
  303.         } else {
  304.           cputs("ok\n\15");
  305.         }
  306.         /* fall thru to fnmf */
  307.       }
  308.  
  309.     } else {  /* doit = 0, because the user press 'n' */
  310.  
  311.       cputs("not moved\15\n");
  312.  
  313.     }
  314.  
  315.       } else {    /* now handle the case when destination do exists */
  316.  
  317. #ifdef debug
  318.     printf("dest exists, let's see...\n");
  319. #endif
  320.  
  321.     if (mydta2.dos.attr & A_DIR) {    /* destination is a directory */
  322. #ifdef debug
  323.       printf("before strcat fdest is %s\n", fdest);
  324.       printf("now cat with %13.13s\n", mydta1.dos.fn);
  325. #endif
  326.  
  327.       /* create destination + tail of source in fdest2
  328.       ** e.g.      source = \user\peter\cat
  329.       **      destination = \junk\haha
  330.       **           fdest2 = \junk\haha\cat
  331.       */
  332.       strcpy(fdest2, fdest);  /* make a work copy */
  333.       catpath(fdest2, mydta1.dos.fn);
  334.  
  335.       if (stype & A_INT) {    /* interactive mode */
  336.         doit = prompt(fsource,fdest2);
  337.       } else {  /* in batch mode, always do it */
  338.         putn(fsource, " ── ", fdest2, "  ", 0);
  339.         doit = 1;  /* do it! */
  340.       }
  341.  
  342.       if (doit) {
  343.  
  344. #ifdef debug
  345.         printf("dest+tail of source: %s\n", fdest2);
  346. #endif
  347.  
  348.         if (mydta1.dos.attr & A_DIR) {  /* source is also a directory */
  349.  
  350.           status = mvdir(mydta1, fsource, fdest2);
  351.           if (!status) {
  352.         cputs("OK.\n\15");
  353.           }
  354.           /* fall down to fnmf */
  355.  
  356.         } else {  /* move a file into a directory */
  357.  
  358.           bset(0);    /* turn break off (disable break key) */
  359.           status = rename(fdest2, fsource);
  360.           bset(brk_st);  /* set break status to orginal value */
  361.  
  362.           if (status) {
  363.         cputs("can't; file exists already/disk write-protected\n\15");
  364.           } else {
  365.         cputs("ok.\n\15");
  366.           }
  367.         }
  368.       } else {  /* doit = 0 */
  369.         cputs("not moved\15\n");
  370.       }
  371.  
  372.     } else {  /* dest is an existing file! */
  373.  
  374.       putn(fsource, " ── ", fdest,
  375.            "   can't rename: file exists already\n\15", 0);
  376.  
  377.     }
  378.  
  379.       }  /* if dest exist or not */
  380.  
  381.       status = fnmf(&mydta1);  /* find info on next matching source */
  382.  
  383.     } while (status == 0);  /* while there are matching files */
  384.   }  /* for different sources */
  385.  
  386.   exit(0);  /* no error */
  387. }
  388.  
  389. prompt(s,d)  /* return 1 for 'y'; 0 for 'n' */
  390. char *s, *d; /* part of the prompt */
  391. {
  392.   char c;
  393.  
  394.   do {
  395.     putn(s, " ── ", d, " (y/n/q; ?=help): ", 0);
  396.     c = getkey("yYnNqQ?");
  397.     putch(' ');
  398.     switch (c) {
  399.  
  400.       case '?':
  401.     putn("\15\n",
  402.          "y - yes, move it\15\n",
  403.          "n - no, skip it\15\n",
  404.          "q - quit to DOS\15\n",
  405.          "? - this help message\15\n\n",
  406.          0);
  407.     break;
  408.  
  409.       case 'y':
  410.       case 'Y':
  411.     return 1;
  412.  
  413.       case 'n':
  414.       case 'N':
  415.     return 0;
  416.  
  417.       case 'q':
  418.       case 'Q':
  419.     cputs("Quit.\15\n");
  420.     exit(0);
  421.  
  422.       default:
  423.     cputs("\15\nHow do I get here?\15\n");
  424.     error("prompt",0);
  425.  
  426.     }  /* end switch */
  427.   } while (1);
  428. }
  429.  
  430. char getkey(valid)  /* wait for valid key and echo it */
  431. char *valid;
  432. {
  433.   char c;
  434.   int i;
  435.  
  436.   do {
  437.     c = getch();
  438.     i = index(valid,c);
  439.     if (i > -1) {
  440.       putch(c);  /* echo valid key */
  441.       return c;
  442.     } else {  /* beep at invalid key */
  443.       putch(7);
  444.     }
  445.   } while (1);
  446. }
  447.